#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#define COMMAND_SIZE 128 
#define MAXARGC 128
char Hostname[256];
char *g_argv[MAXARGC]; 
int g_argc = 0;  
// 我的系统的环境变量
int envnum = 100;
// 我的系统的环境变量
char *genv[envnum];

// 重定向类型：无重定向、输入重定向、输出重定向、追加重定向
#define NONE_REDIR 0
#define INPUT_REDIR 1
#define OUTPUT_REDIR 2
#define APPEND_REDIR 3

// 全局变量：记录当前重定向类型和目标文件名
int redir = NONE_REDIR;  // 默认无重定向
std::string filename;    // 重定向的目标文件名

//退出码
int lastcode = 0;
const char *GetUserName()
{
    const char *name = getenv("USER");
    return name == NULL ? "None" : name;
}

const char *GetHostName()
{   
    gethostname(Hostname, sizeof(Hostname));
    return Hostname == NULL ? "None" : Hostname;
}

const char *GetPwd()
{
    const char *pwd = getenv("PWD");
    return pwd == NULL ? "None" : pwd;
}

std::string DirName(const char* pwd){
#define SLASH "/"
    std::string dir = pwd;
    if(dir==SLASH) return dir;
    auto pos = dir.rfind(SLASH);
    if(pos == std::string::npos)return "big?";
    return dir.substr(pos+1);
}

void PCPt(){
    char prompt[COMMAND_SIZE];
#define FORMAT "[%s@%s %s]# "
    snprintf(prompt,sizeof(prompt),FORMAT,GetUserName(),GetHostName(),DirName(GetPwd()).c_str());
    std::cout<<prompt;
    fflush(stdout);
}

bool GetCommandLine(char *out, int size){
    //分割空格获得字符
    if(fgets(out,size,stdin)==NULL) return false;
    out[strcspn(out, "\n")] = '\0';
    if(strlen(out)==0) return false;
    return true;
}

//处理 /a/b/c 将c提取出来除去/a/b/
void CommandParse(char* commandline){
#define sep " "
    g_argc = 0;
    g_argv[g_argc++] = strtok(commandline,sep);
    while((bool)(g_argv[g_argc++] =strtok(nullptr,sep)));
    g_argc--;return;
}

void AddEnv(const char* item){
    int index = 0;
    while(genv[index]) index++;
    genv[index] = (char*)malloc(strlen(item)+1);
    strncpy(genv[index],item,stlen(item)+1);
    genv[++index] = nullptr;
}

//内键命令判断
bool ChechInKeyCommand(){
    std::string cmd = g_argv[0];
    if(cmd = "cd"){
        if(argc== 2){
            chdir(g_argv[1]);
            lastcode = 0;
        }else{
            lastcode = 1;
        }
        return true;   
    }else if(cmd == "echo"){
        if(g_argc == 2){
            // echo $?
            // echo $PATH
            // echo hellO
            if(g_argv[1][0]== '$'){
                if(g_argv[1][1] == '?'){
                    printf("%d\n"，lastcode);
                    lastcode = 0;
                }
            }
            else{
                printf("%s\n",g_argv[1]);
                lastcode =0 ;
            }
        }else{
            lastcode = 3;
        }
        return true;
    }else if(cmd == "export"){
// export也是内建命令
        if(gargc == 2)
        {
            AddEnv(gargv[1]);
            lastcode = 0;
        }
        else
        {
        lastcode = 2;
        }
        return true;
    }else if(cmd == ""){
        ;
    }
    return false;
}

void InitEnv(){
    extern char** environ;
    int index = 0;
    while(environ[index]){
        genv[index]= (char*)malloc(strlen(environ[index]+1));
        strncpy(genv[index],environ[index],strlen(environ[index])+1);
        index++;
    }
    genv[index] =nullptr;
}

void RedirCheck(char cmd[]) {
    redir = NONE_REDIR;  // 重置重定向类型
    filename.clear();    // 重置文件名
    int start = 0;
    int end = strlen(cmd) - 1;  // 从命令行末尾开始查找（重定向符号通常在末尾）

    // 从后向前扫描命令行，寻找重定向符号
    while (end > start) {
        if (cmd[end] == '<') {  // 输入重定向 <
            cmd[end++] = 0;     // 在 < 位置截断命令（让原命令到此结束）
            TrimSpace(cmd, end);  // 跳过 < 后的空格
            redir = INPUT_REDIR;   // 标记为输入重定向
            filename = cmd + end;  // 提取文件名（< 后面的内容）
            break;
        }
        else if (cmd[end] == '>') {  // 输出重定向 > 或 >>
            if (cmd[end - 1] == '>') {  // 连续两个 >，即 >>（追加）
                cmd[end - 1] = 0;       // 在第一个 > 位置截断命令
                redir = APPEND_REDIR;   // 标记为追加重定向
            }
            else {  // 单个 >（覆盖）
                redir = OUTPUT_REDIR;   // 标记为输出重定向
            }
            cmd[end++] = 0;     // 在 > 位置截断命令
            TrimSpace(cmd, end);  // 跳过 > 后的空格
            filename = cmd + end;  // 提取文件名（> 后面的内容）
            break;
        }
        else {
            end--;  // 继续向前查找
        }
    }
}
int Execute() {
    pid_t id = fork();  // 创建子进程执行命令
    if (id == 0) {  // 子进程
        int fd = -1;  // 用于打开文件的文件描述符

        // 根据重定向类型打开文件，并修改标准输入/输出
        if (redir == INPUT_REDIR) {  // 输入重定向 <
            // 只读方式打开文件（若文件不存在则失败）
            fd = open(filename.c_str(), O_RDONLY);
            if (fd < 0) exit(1);  // 打开失败则退出
            dup2(fd, 0);  // 将标准输入（0）重定向到 fd（文件）
        } 
        else if (redir == OUTPUT_REDIR) {  // 输出重定向 >（覆盖）
            // 创建文件（若不存在），只写，覆盖原有内容（O_TRUNC）
            fd = open(filename.c_str(), O_CREAT | O_WRONLY | O_TRUNC, 0666);
            if (fd < 0) exit(2);
            dup2(fd, 1);  // 将标准输出（1）重定向到 fd（文件）
        } 
        else if (redir == APPEND_REDIR) {  // 追加重定向 >>
            // 创建文件（若不存在），只写，追加内容（O_APPEND）
            fd = open(filename.c_str(), O_CREAT | O_WRONLY | O_APPEND, 0666);
            if (fd < 0) exit(2);
            dup2(fd, 1);  // 将标准输出（1）重定向到 fd（文件）
        }

        // 关闭打开的文件描述符（已通过 dup2 复制，不再需要原 fd）
        if (fd != -1) close(fd);

        // 执行命令（此时输入/输出已被重定向）
        execvp(g_argv[0], g_argv);
        exit(1);  // 若 execvp 失败则退出
    }

    // 父进程等待子进程结束
    int status = 0;
    pid_t rid = waitpid(id, &status, 0);
    if (rid > 0) {
        lastcode = WEXITSTATUS(status);  // 记录命令退出码
    }
    return 0;
}
//int Execute(){
//    pid_t id = fork();
//    if(id==0){
//        execvp(g_argv[0], g_argv);
//        exit(1);
//    }
//
//    pid_t rid = waitpid(id,nullptr,0);
//    (void)rid;//无意义单纯避免编译器优化
//    return 0;
//} 
int main()
{
    while(1){
    //1.输出命令行提示符
    PCPt();
    // 2. 获取用户输入的命令                                                                          
    char commandline[COMMAND_SIZE];                                                                                                                                                    
    if(!GetCommandLine(commandline, sizeof(commandline)))                                             
       continue;                                                                                     
    //std::cout<<commandline<<std::endl;                                                                     
    // 3. 命令行分析 "ls -a -l" -> "ls" "-a" "-l"                                                     
    CommandParse(commandline);                                                                      
    //PrintArgv();                                                                                    
    
    RedirCheck(commandline);
    //处理内键命令
    if(ChechInKeyCommand())
        continue;

    // 4. 执行命令                                                                                       
    Execute(); 
    }
    return 0;
}

